#include "updateresource.h"

//bool copyDir(const QString& fromDirPath, const QString& toDirPath, bool bCoverIfFileExists);
//void deleteDirectory(QFileInfo fileList);
//void clearDirectory(const QString dirName);
//QStringList findCEPro(const QString& usbPath);


UpdateResource::UpdateResource(QObject *parent) :
    QObject(parent)
{
    m_umountCount = 0;
    xmlParser = new XmlParser(this);
    m_umountTimer = new QTimer(this);
    m_umountTimer->setSingleShot(true);
    connect(m_umountTimer, SIGNAL(timeout()), this, SLOT(umountUsb()));
}


void UpdateResource::setDriver(const QString &usbPath)
{
    m_usbPath = usbPath;
}

void UpdateResource::setUpdatePath(const QString& usbPath, const QString& backupPath)
{
    m_usbPath = usbPath;
    m_backUpPath = backupPath;
}

void UpdateResource::startToUpdate()
{
    ProjectStruct projectStruct;
    projectStruct.publishTime = "-1";

//    m_backUpPath = g_installPath + "/" + BACKUPNAME;
    QString path = m_usbPath;
    m_usbPathList.clear();

    //linux自动挂载U盘时，如果出现空格将会转换成"\040",所以要先将他转换成空格，得到正确的路径
    if (path.contains("\\040"))
    {
        path.replace("\\040", " ");
    }
    m_usbPathList.append(path);

    QDir dir(path);

    if (dir.exists())
    {
        QString updateXmlPath = path + "/";
        QStringList xmlList = findCEPro(path);

        if (xmlList.isEmpty())
        {
            emit updateFinish(-1, projectStruct);
        } else {            
            for(int i = 0; i < xmlList.count(); i++)
            {
                ProjectStruct pro = xmlParser->parsetProFile(xmlList.at(i));
                if (projectStruct.publishTime == "-1")
                {
                    projectStruct = pro;
                } else {
                    if (projectStruct.publishTime < pro.publishTime)
                    {
                        projectStruct = pro;
                    }
                }
            }
            //开始更新，通知pisplayer模块，界面显示提醒
            emit startToUpdate(xmlList.count(), projectStruct.publishTime);
            this->updateFile(projectStruct);
        }
    }
}

/*******************************************
  功能：更新资源主要函数，先备份旧资源文件然后更新
        更新完成后或者更新出错恢复后，200ms后自动卸载usb设备
  参数：
  返回值：
  ******************************************/
int UpdateResource::updateFile(const ProjectStruct &proStruct)
{
    QString appPath = g_installPath;
    QString publishTime = proStruct.publishTime;
    QFileInfo info1(proStruct.dbPath);
    QString dbName = info1.fileName();
    QString proPath = proStruct.proPath;
    QString resourcePath = proStruct.resoucePath;

    QString currentVersion = configClass.getScheduleVersion();


    if (proStruct.proPath.isEmpty())
    {
        emit updateFinish(-1, proStruct);
        m_umountTimer->start(200);
        return -1;
    } else {
        if (currentVersion.toLongLong() > publishTime.toLongLong())
        {
            emit updateFinish(-2, proStruct);
            m_umountTimer->start(200);
            return -2;
        } else {
            QDir dir;
            QFile file;
            //从USB中的pro文件中读出的数据得到USB中可以更新的数据路径
            QFileInfo info(proPath);
            QString usbDBPath = info.absolutePath() + "/" + dbName;
            QString usbResourcePath = info.absolutePath() + "/" + resourcePath;
            QString usbProPath = proPath;

            //检查USB中的文件是否齐全
            if (!file.exists(usbDBPath) || !dir.exists(usbResourcePath))
            {
                emit updateFinish(-6, proStruct);
                m_umountTimer->start(200);
                return -6;
            } else {

                //从pro文件中读出当前的资源文件名和数据库名
                QString proName = configClass.getScheduleProName() + ".CEPrj";
                QString curProPath = appPath+ "/" + proName;
                QString curResourceName;
                QString curDBName;
                QString curPublishTime;

                //解析当前pro文件,如果正常,那么开始备份,否则不进行备份操作
                if (xmlParser->parserFile(curProPath, curPublishTime, curDBName, curResourceName) >= 0)
                {
                    QString curDBPath = appPath + "/" + curDBName;
                    QString curRePath = appPath + "/" + curResourceName;

                    if (file.exists(curDBPath) && dir.exists(curRePath))
                    {
                        //先将备份文件清除，然后开始备份，再进行更新
                        clearDirectory(m_backUpPath);
                        //备份当前版本
                        backUpData();
                    }
                    if (file.exists(curProPath))
                    {
                        file.remove(curProPath);
                    }

                    if (file.exists(curDBPath))
                    {
                        file.remove(curDBPath);
                    }

                    if(dir.exists(curRePath))
                    {
                        clearDirectory(curRePath);
                    }
                }

                //得到要更新的路径
                QString updateDBPath = appPath + "/" + dbName;
                QString updateResourcePath = appPath + "/" + resourcePath;
                QString updateProPath = appPath + "/" + info.fileName();

                //首先要检查是否要更新的文件在原本的数据是否存在，把他们删除
                if (file.exists(updateDBPath))
                {
                    file.remove(updateDBPath);
                }
                if (file.exists(updateProPath))
                {
                    file.remove(updateProPath);
                }

                if(dir.exists(updateResourcePath))
                {
                    clearDirectory(updateResourcePath);
                }

                //开始更新，首先更新pro和数据库，接着更新资源文件
                if (!file.copy(usbDBPath, updateDBPath))
                {
                    emit updateFinish(-3, proStruct);
                    this->getBackUpData();
                    m_umountTimer->start(200);
                    return -3;
                }

                if (!copyDir(usbResourcePath, updateResourcePath, true))
                {
                    emit updateFinish(-4, proStruct);
                    this->getBackUpData();
                    m_umountTimer->start(200);
                    return -4;
                }

                if (!file.copy(usbProPath, updateProPath))
                {
                    emit updateFinish(-3, proStruct);
                    this->getBackUpData();
                    m_umountTimer->start(200);
                    return -3;
                }

//                qDebug() << "update file==>" << proStruct.resourceSize;
                currentVersion = publishTime;
                configClass.setScheduleVersion(currentVersion);
                configClass.setScheduleProName(info.baseName());
                configClass.setResourceSize(proStruct.resourceSize);
                configClass.sync();

                emit updateFinish(0, proStruct);
            }
        }
    }
    m_umountTimer->start(3 * 1000);
    return 1;
}


/*******************************************
  功能：备份当前资源文件
  参数：
  返回值：
  ******************************************/
bool UpdateResource::backUpData() const
{
    QString backUpPath = g_installPath + "/" + BACKUPNAME;

    QDir dir(backUpPath);
    if (!dir.exists())
    {
        dir.mkdir(backUpPath);
    }


    //从pro文件中读出当前的资源文件名和数据库名
    QString appPath = g_installPath;
    QString proName = configClass.getScheduleProName() + ".CEPrj";
    QString curProAbPath = appPath+ "/" + proName;
    QString curResourceName;
    QString curDBName;
    QString curPublishTime;

//    xmlParser.parserFile(curProAbPath, curPublishTime, curDBName, curResourceName);
    if (xmlParser->parserFile(curProAbPath, curPublishTime, curDBName, curResourceName) < 0)
    {
        return false;
    }
    QString curDBPath = appPath + "/" + curDBName;
    QString curRePath = appPath + "/" + curResourceName;

    //将相应的APP路径换成备份路径，然后将相应的pro，数据库文件和资源文件拷贝过去
    QString backUpDBPath = backUpPath + "/" + curDBName;
    QString backUpResourcePath = backUpPath + "/" + curResourceName;
    QString backUpProPath = backUpPath + "/" + proName;

    clearDirectory(backUpPath);

    QFile file(backUpProPath);
    if (file.exists())
    {
        file.remove(backUpProPath);
    }
    file.copy(curProAbPath, backUpProPath);
//    file.copy(curDBPath, backUpDBPath);


    QFile file1(backUpDBPath);
    if (file1.exists())
    {
        file1.remove(backUpDBPath);
    }
    file1.copy(curDBPath, backUpDBPath);

    return copyDir(curRePath, backUpResourcePath, true);
}

/*******************************************
  功能:拷贝备份文件，进行恢复
  参数：
  返回值：
  ******************************************/
bool UpdateResource::getBackUpData()
{
    QFile file;
    QString currentVersion;
    ProjectStruct pro;
    QDir dir(m_backUpPath);
    if (!dir.exists(m_backUpPath))
    {
        emit updateFinish(-7, pro);
        return false;
    }


    //接着在备份文件中找出备份的pro文件，然后读出备份的数据名和资源名字，得到备份的文件的相关路径
    QStringList prolist = findCEPro(m_backUpPath);
    QString resourceName;
    QString dbName;
    QString publishTime;

    if (prolist.count() == 1)
    {
        int returnErr = xmlParser->parserFile(prolist.at(0), publishTime, dbName, resourceName);
        if (returnErr < 0)
            return false;
    } else if (prolist.count() <= 0)
    {
        emit updateFinish(-7, pro);
        return false;
    }

    QString backUpDBPath = m_backUpPath + "/" + dbName;
    QString backUpResourcePath = m_backUpPath + "/" + resourceName;


    if (!file.exists(backUpDBPath) || !dir.exists(backUpResourcePath))
    {
        return false;
    }

    //从pro文件中读出当前的资源文件名和数据库名
    QString appPath = g_installPath;
    QString proName = configClass.getScheduleProName() + ".CEPrj";
    QString curProAbPath = appPath+ "/" + proName;
    QString curResourceName;
    QString curDBName;
    QString curPublishTime;

    if (xmlParser->parserFile(curProAbPath, curPublishTime, curDBName, curResourceName) >= 0)
    {
        //首先将当前的资源先清空，然后再进行备份还原
        QString curDBPath = appPath + "/" +  curDBName;
        QString curResourcePath = appPath + "/" + curResourceName;

        if (file.exists(curDBPath))
        {
            file.remove(curDBPath);
        }

        dir = QDir(curResourcePath);
        if (dir.exists())
        {
            clearDirectory(curResourcePath);
            dir.rmdir(curResourcePath);
        }
    }

    //接着得到要还原的路径，一般就在APP当前路径下加上相应的资源文件路径
    QFileInfo info(prolist.at(0));
    QString returnProPath = appPath + "/" + info.fileName();
    QString returnDBPath = appPath + "/" + dbName;
    QString returnRePath = appPath + "/" + resourceName;

    //备份数据开始还原
    QFile file1(returnProPath);
    if (file1.exists())
    {
        file1.remove(returnProPath);
    }

    file1.copy(prolist.at(0), returnProPath);

    if (file1.exists(returnDBPath))
    {
        file1.remove(returnDBPath);
    }

    file1.copy(backUpDBPath, returnDBPath);

    copyDir(backUpResourcePath, returnRePath, true);

    currentVersion = publishTime;
    configClass.setScheduleVersion(currentVersion);
    configClass.setScheduleProName(info.baseName());

     return true;
}

//卸载U盘,先逐个U盘卸载,如果全部卸载完成,那么完成,如果没有,那么再启动卸载定时器,间隔2秒后继续卸载
void UpdateResource::umountUsb()
{
//    qDebug() << "=========================<<<UpdateResource:umountUsb>>>==============================";
    int len;

#ifdef _TTY_POSIX_
    for(int i = 0; i < m_usbPathList.count(); i++)
    {
//        qDebug() << "-------------- umountUsb----------------";
        QString usbPath = m_usbPathList.at(i);
        pid_t child;

        //同步数据，即将数据写入到Ｕ盘中，如果没有此命令，那么卸载Ｕ盘时
        //会重新写入数据，这样强制卸载Ｕ盘会造成数据丢失
//        len = system("sync");
        sync();

        child = fork();
        if (child == 0)
        {
            char mountBuf[128] = {0};
            strcpy(mountBuf, usbPath.toLatin1().data());
//            qDebug() << "child==============> fork==============================<" << mountBuf;

            //exec函数，调用shell命令，如果调用成功，不返回任何值，失败返回-1
            execl("/bin/umount", "umount", mountBuf, NULL);

            exit(0);

        }
        int waitStatus;
        waitpid(-1, &waitStatus, NULL);

        if (m_umountCount > UMOUNTMAXCOUNT)
        {
//            qDebug() << "umount fail--------------------";
            emit umountUsbState(-1);
            m_usbPathList.removeAt(i);
            i--;
            emit finish();
            return;
        }

        QDir dir(usbPath);
        if (dir.exists())
        {
//            qDebug() << "dir is exists===================================>>>";

            if (m_umountCount)
                m_umountCount++;
        } else {
//            qDebug() << "dir isn`t exists===================================>>>";
            m_umountCount = 0;
//            emit finish();
            emit umountUsbState(0);
            m_usbPathList.removeAt(i);
            i--;
        }

    }

    if (m_usbPathList.count() > 0)
    {
        m_umountTimer->start(2 * 1000);
    } else {
        m_umountTimer->stop();
        emit finish();
    }
#endif
}



bool copyDir(const QString& fromDirPath, const QString& toDirPath, bool bCoverIfFileExists)
{
//    qDebug() << "copyDir==============>" << fromDirPath << toDirPath << bCoverIfFileExists;
    QDir formDir(fromDirPath);
    QDir toDir(toDirPath);

    if(!toDir.exists())
    {
        if(!toDir.mkdir(toDirPath))
            return false;
    }

    QFileInfoList fileInfoList = formDir.entryInfoList();
    foreach(QFileInfo fileInfo, fileInfoList)
    {
        if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;

        //拷贝子目录
        if(fileInfo.isDir())
        {
            //递归调用拷贝
            QString toFilePath = toDirPath + "/" +  fileInfo.fileName();

            if(!copyDir(fileInfo.filePath(), toFilePath, bCoverIfFileExists))
                return false;
        }
        //拷贝子文件
        else
        {
            if(bCoverIfFileExists && toDir.exists(fileInfo.fileName()))
            {
                toDir.remove(fileInfo.fileName());
            }
            if(!QFile::copy(fileInfo.filePath(), toDir.filePath(fileInfo.fileName())))
            {
                return false;
            }
        }
    }
    return true;
}


QStringList findCEPro(const QString& usbPath)
{
    QDir dir(usbPath);

    QFileInfoList fileInfoList = dir.entryInfoList();
    QStringList fileList;

    foreach(QFileInfo fileInfo, fileInfoList)
    {
        if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;

        //查找子目录
        if(fileInfo.isDir())
        {
//            qDebug() << "fileInfo============== is dir" << fileInfo.filePath();
            //递归调用拷贝
            QStringList list = findCEPro(fileInfo.filePath());
            if (!list.isEmpty())
            {
                fileList.append(list);
            }
        }
        //查找子文件
        else
        {
//            qDebug() << "fileInfo============== is file" << fileInfo.filePath();
            if (fileInfo.fileName().contains(".CEPrj"))
            {
                fileList.append(fileInfo.absoluteFilePath());
            }
        }
    }
    return fileList;
}

void clearDirectory(const QString dirName)
{

    QDir dir(dirName);
    QFileInfoList fileInfoList = dir.entryInfoList();

    foreach(QFileInfo fileInfo, fileInfoList)
    {
        if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
            continue;


        //删除子目录
        if(fileInfo.isDir())
        {
            //递归调用清除文件夹
            clearDirectory(fileInfo.filePath());
            QDir dir1;
            dir1.rmdir(fileInfo.filePath());
        }
        //删除子文件
        else
        {
            QFile file;
            file.remove(fileInfo.absoluteFilePath());
        }
    }
}

void deleteDirectory(QFileInfo fileList)
{
    if(fileList.isDir())
    {
        int childCount =0;
        QString dir = fileList.filePath();
        QDir thisDir(dir);
        childCount = thisDir.entryInfoList().count();
        QFileInfoList newFileList = thisDir.entryInfoList();
        if(childCount>2)
        {
            for(int i=0;i<childCount;i++)
            {
                if(newFileList.at(i).fileName().operator ==(".")|newFileList.at(i).fileName().operator ==(".."))
                {
                    continue;
                }
                deleteDirectory(newFileList.at(i));
            }
        }
        fileList.absoluteDir().rmpath(fileList.fileName());
    }
    else if(fileList.isFile())
    {
        fileList.absoluteDir().remove(fileList.fileName());
    }
}
